#include "FQFMedia.h"
#include "FQF.h"
#include "configure.h"

FQFMedia::FQFMedia()
{
	FQF::getObject();
    bRtspTransport = Configure::getObject()->getRtspConnectType();
	errorbuf[0] = '\0';
	locker = new QMutex;
}

FQFMedia::~FQFMedia()
{

}
//超时回掉函数
static int CheckInterrupt(void* pointer)
{
    auto p = (FQFMedia *)pointer;
	int timeout = p->isReadPush() ? p->getReadPushTimeout() : p->getOpenTimeout();
    if (time(nullptr) - p->getStartTime() >= timeout)
	{
		p->setTimeoutState(false);
		return 1;
	}
	else
		return 0;
}

bool FQFMedia::openInStream(const char * url, StreamOpenMode openMode)
{
	closeStream();
	locker->lock();
	
    AVDictionary *options = nullptr;
    av_dict_set(&options, "rtsp_transport", bRtspTransport ? "tcp" : "udp", 0);

	inFmtCtx = avformat_alloc_context();
	inFmtCtx->interrupt_callback.callback = CheckInterrupt;//超时回调
	inFmtCtx->interrupt_callback.opaque = this;

	bIsReadPush = false;
    startTime = time(nullptr);
    int re;
	//打开视频流
    re = avformat_open_input(&inFmtCtx, url, nullptr, &options);
	if (re != 0)
	{
		//失败
		av_strerror(re, errorbuf, sizeof(errorbuf));
		locker->unlock();
		return false;
    }
    strcpy_s(rtspUrl, url);
	if (openMode & StreamOpenMode::PlayOnly)
	{
		//遍历视频中的流信息，保存视频流信息
        for (int i = 0; i < static_cast<int>(inFmtCtx->nb_streams); i++)
		{
			//视频流
            if (inFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
			{
				iVideoStream = i;
				//查找视频解码器
                AVCodec *codec = avcodec_find_decoder(inFmtCtx->streams[i]->codecpar->codec_id);
				if (!codec)
				{
                    strcpy_s(errorbuf, "stream video code not find!");
					locker->unlock();
					return false;
				}
                videoContext = avcodec_alloc_context3(codec);
				//打开视频解码器
                re = avcodec_open2(videoContext, codec, nullptr);
				if (re != 0)
				{
					av_strerror(re, errorbuf, sizeof(errorbuf));
					locker->unlock();
					return false;
				}
			}
            else if (inFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
			{
				iAudioStream = i;
				//查找视频解码器
                AVCodec *codec = avcodec_find_decoder(inFmtCtx->streams[i]->codecpar->codec_id);
				if (!codec)
				{
                    strcpy_s(errorbuf, "stream audio code not find!");
					locker->unlock();
					return false;
				}
                audioContext = avcodec_alloc_context3(codec);
				//打开视频解码器
                re = avcodec_open2(audioContext, codec, nullptr);
				if (re != 0)
				{
					av_strerror(re, errorbuf, sizeof(errorbuf));
					locker->unlock();
					return false;
				}
			}
		}
		bTimeoutState = true;
	}
	
	locker->unlock();
	return true;
}

bool FQFMedia::openOutStream(const char *url)
{
	locker->lock();
    if (!streamOpenMode & StreamOpenMode::PushOnly)
	{
        strcpy_s(errorbuf, "stream open only play!");
		locker->unlock();
		return false;
	}
	int re;

    re = avformat_find_stream_info(inFmtCtx, nullptr);
	if (re != 0)
	{
		av_strerror(re, errorbuf, sizeof(errorbuf));
		locker->unlock();
		return false;
	}

    re = avformat_alloc_output_context2(&outFmtCtx, nullptr, "flv", url);
	outFmtCtx->interrupt_callback.callback = CheckInterrupt;//超时回调
	outFmtCtx->interrupt_callback.opaque = this;

	if (!outFmtCtx)
	{
        strcpy_s(errorbuf, "stream out open not alloc!");
		locker->unlock();
		return false;
	}
	//配置输出流
	//遍历输入的AVStream
    for (int i = 0; i < static_cast<int>(inFmtCtx->nb_streams); i++)
	{
		//创建输出流
        AVStream *out = avformat_new_stream(outFmtCtx, inFmtCtx->streams[i]->codec->codec);
		if (!out)
		{
            strcpy_s(errorbuf, "stream alloc failed!");
			locker->unlock();
			return false;
		}
		//复制配置信息,
		//旧，用于MP4
		//re = avcodec_copy_context(out->codec, ictx->streams[i]->codec);
		re = avcodec_parameters_copy(out->codecpar, inFmtCtx->streams[i]->codecpar);
		//旧
//		out->codec->codec_tag = 0;
		out->codecpar->codec_tag = 0;
	}
	bIsReadPush = false;
    startTime = time(nullptr);
	//打开io
	re = avio_open(&outFmtCtx->pb, url, AVIO_FLAG_WRITE);
	if (!outFmtCtx->pb)
	{
		av_strerror(re, errorbuf, sizeof(errorbuf));
		locker->unlock();
		return false;
	}
    strcpy_s(rtmpUrl, url);
	//写入头信息
    re = avformat_write_header(outFmtCtx, nullptr);
	if (re < 0)
	{
		av_strerror(re, errorbuf, sizeof(errorbuf));
		locker->unlock();
		return false;
	}
	bTimeoutState = true;
	locker->unlock();
	return true;
}

void FQFMedia::closeStream()
{
	locker->lock();
	bTimeoutState = false;
	if (inFmtCtx)
		avformat_close_input(&inFmtCtx);
	if (outFmtCtx)
	{
		avio_close(outFmtCtx->pb);
		avformat_free_context(outFmtCtx);
        outFmtCtx = nullptr;
	}
	if (packet)
	{
		av_free_packet(packet);
        packet = nullptr;
	}
	if (videoFrame)
		av_frame_free(&videoFrame);
	if (audioFrame)
		av_frame_free(&audioFrame);
	if (videoSwsCtx)
	{
		sws_freeContext(videoSwsCtx);
        videoSwsCtx = nullptr;
	}
	locker->unlock();
}

bool FQFMedia::readStream()
{
	locker->lock();
	
	if (!inFmtCtx)
	{
        strcpy_s(errorbuf, "stream not open!");
		
		if (readErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}

	if (!packet)
	{
		packet = av_packet_alloc();
	}

	bIsReadPush = true;
    startTime = time(nullptr);

	//读取
	int re = av_read_frame(inFmtCtx, packet);
	if (re != 0)
	{
		//失败
		av_strerror(re, errorbuf, sizeof(errorbuf));
		if (readErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}
	lastPacketIndex = packet->stream_index;
	readErrorNum = 0;
	locker->unlock();
	return true;
}

bool FQFMedia::pushPacket()
{
	locker->lock();
	if (!inFmtCtx)
	{
        strcpy_s(errorbuf, "inStream not open!");
		if (pushErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}
	if (!outFmtCtx)
	{
        strcpy_s(errorbuf, "outStream not open!");
		if (pushErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}
	if (!packet)
	{
        strcpy_s(errorbuf, "inStream not read!");
		if (pushErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}
	if (packet->size <= 0)
	{
        strcpy_s(errorbuf, "stream read failed!");
		if (pushErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}

	//计算转换pts dts
	AVRational itime = inFmtCtx->streams[packet->stream_index]->time_base;
	AVRational otime = outFmtCtx->streams[packet->stream_index]->time_base;
	packet->pts = av_rescale_q_rnd(packet->pts, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
	packet->dts = av_rescale_q_rnd(packet->dts, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
	packet->duration = av_rescale_q_rnd(packet->duration, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
	packet->pos = -1;

	bIsReadPush = true;
    startTime = time(nullptr);
	int re = av_interleaved_write_frame(outFmtCtx, packet);
	if (re < 0)
	{
		av_strerror(re, errorbuf, sizeof(errorbuf));
		if (pushErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}
	pushErrorNum = 0;
	locker->unlock();
	return true;
}

bool FQFMedia::decodePacket()
{
	locker->lock();
	if (!inFmtCtx)
	{
        strcpy_s(errorbuf, "stream not open!");
		if (decodeErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}
	if (!packet)
	{
        strcpy_s(errorbuf, "stream not read!");
		if (decodeErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}
	if (packet->size <= 0)
	{
        strcpy_s(errorbuf, "stream read failed!");
		if (decodeErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}
	
	//解码，新版本
	//发送pkt
	int re = avcodec_send_packet(inFmtCtx->streams[packet->stream_index]->codec, packet);
	if (re != 0)
	{
		av_strerror(re, errorbuf, sizeof(errorbuf));
		if (decodeErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}
	if (packet->stream_index == iVideoStream)
	{
        if (videoFrame == nullptr)
		{
			videoFrame = av_frame_alloc();
		}
		//获取frame
		re = avcodec_receive_frame(inFmtCtx->streams[packet->stream_index]->codec, videoFrame);
		if (re != 0)
		{
			av_strerror(re, errorbuf, sizeof(errorbuf));
			if (decodeErrorNum++ > 10)
				bTimeoutState = false;
			locker->unlock();
			return false;
		}
		iWidth = videoFrame->width;
		iHeight = videoFrame->height;
	}
	else if (packet->stream_index == iAudioStream)
	{
        if (audioFrame == nullptr)
		{
			audioFrame = av_frame_alloc();
		}
		//获取frame
		re = avcodec_receive_frame(inFmtCtx->streams[packet->stream_index]->codec, audioFrame);
		if (re != 0)
		{
			av_strerror(re, errorbuf, sizeof(errorbuf));
			if (decodeErrorNum++ > 10)
				bTimeoutState = false;
			locker->unlock();
			return false;
		}
	}
	else
	{
		locker->unlock();
		return false;
	}
	decodeErrorNum = 0;
	locker->unlock();
	return true;
}

bool FQFMedia::yuvToRgb(char * outSpace, int outWidth, int outHeight)
{
	locker->lock();
	if (!inFmtCtx || !videoFrame)
	{
        strcpy_s(errorbuf, "stream not open or videoFrame not decoded!");
		locker->unlock();
		return false;
	}
	//设置转码器
	AVCodecContext * videoCtx = inFmtCtx->streams[this->iVideoStream]->codec;
	videoSwsCtx = sws_getCachedContext(videoSwsCtx,
		videoCtx->width, videoCtx->height, videoCtx->pix_fmt,
		outWidth, outHeight, AV_PIX_FMT_BGRA,
        SWS_BICUBIC, nullptr, nullptr, nullptr
	);
	if (!videoSwsCtx)
	{
		//失败
        strcpy_s(errorbuf, "sws getCachedContext failed!");
		if (swsErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}
	//转码输出空间
    uint8_t *data[AV_NUM_DATA_POINTERS] = { nullptr };
	data[0] = (uint8_t *)outSpace;
	//图像宽度
	int linesize[AV_NUM_DATA_POINTERS] = { 0 };
	linesize[0] = outWidth * 4;
	//转码，成功后返回转码图像的高
	int h = sws_scale(videoSwsCtx, videoFrame->data, videoFrame->linesize, 0, videoCtx->height,
		data, linesize);
	if (h <= 0)
	{
		//失败
        strcpy_s(errorbuf, "sws scale failed!");
		if (swsErrorNum++ > 10)
			bTimeoutState = false;
		locker->unlock();
		return false;
	}
	swsErrorNum = 0;
	locker->unlock();
    return true;
}

bool FQFMedia::rgbToYun()
{
    locker->lock();
    if (!inFmtCtx || !videoFrame)
    {
        strcpy_s(errorbuf, "stream not open or videoFrame not decoded!");
        locker->unlock();
        return false;
    }
    //设置转码器
    AVCodecContext * videoCtx = inFmtCtx->streams[this->iVideoStream]->codec;
    videoSwsCtx = sws_getCachedContext(videoSwsCtx,
        videoCtx->width, videoCtx->height, videoCtx->pix_fmt,
        videoCtx->width, videoCtx->height, AV_PIX_FMT_YUV420P,
        SWS_BICUBIC, nullptr, nullptr, nullptr
    );
    if (!videoSwsCtx)
    {
        //失败
        strcpy_s(errorbuf, "sws getCachedContext failed!");
        if (swsErrorNum++ > 10)
            bTimeoutState = false;
        locker->unlock();
        return false;
    }
    if (!outFrame)
    {
        outFrame = av_frame_alloc();
        outFrame->width = videoFrame->width;
        outFrame->height = videoFrame->height;
        outFrame->format = AV_PIX_FMT_YUV420P;
        int re = av_frame_get_buffer(outFrame, 32);
        if (re < 0)
        {
            av_strerror(re, errorbuf, sizeof(errorbuf));
            locker->unlock();
            return false;
        }
    }
    //转码，成功后返回转码图像的高
    int h = sws_scale(videoSwsCtx, videoFrame->data, videoFrame->linesize, 0, videoCtx->height,
        outFrame->data, outFrame->linesize);
    if (h <= 0)
    {
        //失败
        strcpy_s(errorbuf, "sws scale failed!");
        if (swsErrorNum++ > 10)
            bTimeoutState = false;
        locker->unlock();
        return false;
    }
    swsErrorNum = 0;
    locker->unlock();
    return true;
}

bool FQFMedia::encodePacket()
{
    locker->lock();
    if (!inFmtCtx)
    {
        strcpy_s(errorbuf, "stream not open!");
        if (decodeErrorNum++ > 10)
            bTimeoutState = false;
        locker->unlock();
        return false;
    }
    if (!packet)
    {
        strcpy_s(errorbuf, "stream not read!");
        if (decodeErrorNum++ > 10)
            bTimeoutState = false;
        locker->unlock();
        return false;
    }
    if (packet->size <= 0)
    {
        strcpy_s(errorbuf, "stream read failed!");
        if (decodeErrorNum++ > 10)
            bTimeoutState = false;
        locker->unlock();
        return false;
    }

    //解码，新版本
    //发送pkt
    int re = avcodec_send_packet(inFmtCtx->streams[packet->stream_index]->codec, packet);
    if (re != 0)
    {
        av_strerror(re, errorbuf, sizeof(errorbuf));
        if (decodeErrorNum++ > 10)
            bTimeoutState = false;
        locker->unlock();
        return false;
    }
    if (packet->stream_index == iVideoStream)
    {
        if (videoFrame == nullptr)
        {
            videoFrame = av_frame_alloc();
        }
        //获取frame
        re = avcodec_receive_frame(inFmtCtx->streams[packet->stream_index]->codec, videoFrame);
        if (re != 0)
        {
            av_strerror(re, errorbuf, sizeof(errorbuf));
            if (decodeErrorNum++ > 10)
                bTimeoutState = false;
            locker->unlock();
            return false;
        }
        iWidth = videoFrame->width;
        iHeight = videoFrame->height;
    }
    else if (packet->stream_index == iAudioStream)
    {
        if (audioFrame == nullptr)
        {
            audioFrame = av_frame_alloc();
        }
        //获取frame
        re = avcodec_receive_frame(inFmtCtx->streams[packet->stream_index]->codec, audioFrame);
        if (re != 0)
        {
            av_strerror(re, errorbuf, sizeof(errorbuf));
            if (decodeErrorNum++ > 10)
                bTimeoutState = false;
            locker->unlock();
            return false;
        }
    }
    else
    {
        locker->unlock();
        return false;
    }
    decodeErrorNum = 0;
    locker->unlock();
    return true;
}

std::string FQFMedia::getError()
{
	locker->lock();
	std::string str = this->errorbuf;
	locker->unlock();
	return str;
}

int FQFMedia::getPacketIndex()
{
	return lastPacketIndex;
}

bool FQFMedia::packetIsVideo()
{
	locker->lock();
	if (iVideoStream == lastPacketIndex)
	{
		locker->unlock();
		return true;
	}
	locker->unlock();
	return false;
}

bool FQFMedia::packetIsAudio()
{
	locker->lock();
	if (iAudioStream == lastPacketIndex)
	{
		locker->unlock();
		return true;
	}
	locker->unlock();
	return false;
}

bool FQFMedia::getYuvSize(int * w, int * h)
{
	if (iWidth == 0 || iHeight == 0)
		return false;
	*w = iWidth;
	*h = iHeight;
    return true;
}

time_t FQFMedia::getStartTime()
{
    return startTime;
}

void FQFMedia::setRtspTransport(bool state)
{
    bRtspTransport = state;
}

bool FQFMedia::getTimeoutState()
{
	return bTimeoutState;
}

void FQFMedia::setTimeoutState(bool state)
{
	bTimeoutState = state;
}

void FQFMedia::setOpenTimeout(int time)
{
	iOpenTimeout = time;
}

int FQFMedia::getOpenTimeout()
{
	return iOpenTimeout;
}

void FQFMedia::setReadPushTimeout(int time)
{
	iReadPushTimeout = time;
}

int FQFMedia::getReadPushTimeout()
{
	return iReadPushTimeout;
}

bool FQFMedia::isReadPush()
{
	return bIsReadPush;
}
